#include "c0vm.h"
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#include "c0runtime.h"

/* for the args library */
int c0_argc;
char **c0_argv;

/* fail-fast file function wrappers */
FILE *really_fopen(const char *filename, const char *mode,
                   char *error)
{
    FILE *f = fopen(filename, mode);
    if (f == NULL) {
        perror(error);
        exit(1);
    }
    return f;
}

void really_fwrite(const void *ptr, size_t size, size_t nitems, FILE *stream,
                    char *error)
{
    if (fwrite(ptr, size, nitems, stream) < nitems) {
        perror(error);
        exit(1);
    }
}

void really_fclose(FILE *stream, char *error)
{
    if (fclose(stream) != 0) {
        perror(error);
        exit(1);
    }
}


int main(int argc, char **argv) {
  if (argc < 2) {
    fprintf(stderr, "usage: %s <bc0_file> [args...]\n", argv[0]);
    exit(1);
  }

  /* test for two's complement */
  if (~(-1) != 0) {
    fprintf(stderr, "Error: not a two's complement machine\n");
    exit(1);
  }

  /* test representation sizes */
  if (sizeof(int) != 4) {
    fprintf(stderr, "Error: sizeof(int) == %zd != 4\n", sizeof(int));
    exit(1);
  }

  if (sizeof(int) != sizeof(void *)) {
    fprintf(stderr, "Error: sizeof(int) == %zd != %zd == sizeof(void *)\n",
            sizeof(int), sizeof(void *));
    exit(1);
  }

  /* test that casting preserves identity for a few crucial values */
  if ( (int) (void *) INT_MIN != INT_MIN
    || (int) (void *) INT_MAX != INT_MAX
    || (int) (void *) -1 != -1
    || (int) (void *) 0 != 0
    || (int) (void *) 1 != 1)
  {
    fprintf(stderr, "Error: casting between int and void * loses information\n");
    exit(1);
  }

  /* for the args library -- skip the binary name */
  c0_argc = argc - 1;
  c0_argv = argv + 1;

  char *filename = getenv("C0_RESULT_FILE");

  /* initialize the runtime -- possibly initializing the GC */
  c0_runtime_init();

  struct bc0_file *bc0 = read_program(argv[1]);

  if (filename == NULL) {
    int result = execute(bc0);
    printf("Result = %d\n", result);
  } else {
    FILE *f = really_fopen(filename, "w", "Couldn't open $C0_RESULT_FILE");
    really_fwrite("\0", 1, 1, f, "Couldn't write to $C0_RESULT_FILE");
    int result = execute(bc0);
    printf("Result = %d\n", result);
    really_fwrite(&result, sizeof(int), 1, f, "Couldn't write to $C0_RESULT_FILE");
    really_fclose(f, "Couldn't close $C0_RESULT_FILE");
  }

  free_program(bc0);
  return 0;
}
